package rdb

import (
	"context"
	"fmt"
	"github.com/redis/go-redis/v9"
	"sync"
)

type QueueStringList interface {
	TopicKey(id any) string
	RedisClient() *redis.Client
	SetQueueStringListMaxLength(max uint64)
	LPush(id any, value string) error
	RPush(id any, value string) error
	LPushList(id any, list []string) error
	RPushList(id any, list []string) error
	LPop(id any) (string, error)
	RPop(id any) (string, error)
	LPopList(id any, count int) ([]string, error)
	RPopList(id any, count int) ([]string, error)
	Count(id any) (int64, error)
	GetAll(id any) ([]string, error)
	ClearAll(id any) error
}

type _queueStringList struct {
	Topic     string        //主题
	Redis     *redis.Client //Redis客户端
	MaxLength uint64        //队列最大容量
	RWL       sync.RWMutex  //读写锁
}

func NewQueueStringList(client *redis.Client, topic string) QueueStringList {
	return &_queueStringList{
		Topic: topic,
		Redis: client,
		RWL:   sync.RWMutex{},
	}
}

// TopicKey 返回默认生成的主题key
func (q *_queueStringList) TopicKey(id any) string {
	return fmt.Sprint("QueueStringList_", q.Topic, "_", id)
}

// RedisClient 返回绑定的Redis客户端
func (q *_queueStringList) RedisClient() *redis.Client {
	return q.Redis
}

// SetQueueStringListMaxLength 队列最大容量长度，0则无限长。
// 超出这个长度的时候，插入数据的同时，会往返方向移出，直到剩余长度等于最大容量长度。
func (q *_queueStringList) SetQueueStringListMaxLength(max uint64) {
	q.RWL.Lock()
	q.MaxLength = max
	q.RWL.Unlock()
}

// LPush 左边插入 1 个
func (q *_queueStringList) LPush(id any, value string) error {
	q.RWL.Lock()
	err := q.Redis.LPush(context.Background(), q.TopicKey(id), value).Err()
	if q.MaxLength > 0 && err == nil {
		result, _ := q.Redis.LLen(context.Background(), q.TopicKey(id)).Result()
		if uint64(result)-q.MaxLength > 0 {
			for i := 0; i < int(uint64(result)-q.MaxLength); i++ {
				q.Redis.RPop(context.Background(), q.TopicKey(id))
			}
		}
	}
	q.RWL.Unlock()
	return err
}

// RPush 右边插入 1 个
func (q *_queueStringList) RPush(id any, value string) error {
	q.RWL.Lock()
	err := q.Redis.RPush(context.Background(), q.TopicKey(id), value).Err()
	if q.MaxLength > 0 && err == nil {
		result, _ := q.Redis.LLen(context.Background(), q.TopicKey(id)).Result()
		if uint64(result)-q.MaxLength > 0 {
			for i := 0; i < int(uint64(result)-q.MaxLength); i++ {
				q.Redis.LPop(context.Background(), q.TopicKey(id))
			}
		}
	}
	q.RWL.Unlock()
	return err
}

// LPushList 左边插入 N 个
func (q *_queueStringList) LPushList(id any, list []string) error {
	q.RWL.Lock()
	var temp []interface{}
	for _, d := range list {
		temp = append(temp, d)
	}
	err := q.Redis.LPush(context.Background(), q.TopicKey(id), temp...).Err()
	if q.MaxLength > 0 && err == nil {
		result, _ := q.Redis.LLen(context.Background(), q.TopicKey(id)).Result()
		if uint64(result)+uint64(len(list))-q.MaxLength > 0 {
			for i := 0; i < int(uint64(result)+uint64(len(list))-q.MaxLength); i++ {
				q.Redis.RPop(context.Background(), q.TopicKey(id))
			}
		}
	}
	q.RWL.Unlock()
	return err
}

// RPushList 右边插入 N 个
func (q *_queueStringList) RPushList(id any, list []string) error {
	q.RWL.Lock()
	var temp []interface{}
	for _, d := range list {
		temp = append(temp, d)
	}
	err := q.Redis.RPush(context.Background(), q.TopicKey(id), temp...).Err()
	if q.MaxLength > 0 && err == nil {
		result, _ := q.Redis.LLen(context.Background(), q.TopicKey(id)).Result()
		if uint64(result)+uint64(len(list))-q.MaxLength > 0 {
			for i := 0; i < int(uint64(result)+uint64(len(list))-q.MaxLength); i++ {
				q.Redis.LPop(context.Background(), q.TopicKey(id))
			}
		}
	}
	q.RWL.Unlock()
	return err
}

// LPop 左边移出 1 个
func (q *_queueStringList) LPop(id any) (string, error) {
	q.RWL.Lock()
	result, err := q.Redis.LPop(context.Background(), q.TopicKey(id)).Result()
	q.RWL.Unlock()
	return result, err
}

// RPop 右边移出 1 个
func (q *_queueStringList) RPop(id any) (string, error) {
	q.RWL.Lock()
	result, err := q.Redis.RPop(context.Background(), q.TopicKey(id)).Result()
	q.RWL.Unlock()
	return result, err
}

// LPopList 左边移出 N 个
func (q *_queueStringList) LPopList(id any, count int) ([]string, error) {
	q.RWL.Lock()
	var result []string
	var j = 0
	for i := 0; i < count; i++ {
		j++
		temp, err := q.Redis.LPop(context.Background(), q.TopicKey(id)).Result()
		if err != nil || j > count*2 {
			if err == redis.Nil || j > count*2 {
				break
			}
			i--
			continue
		}
		result = append(result, temp)
	}
	q.RWL.Unlock()
	if len(result) <= 0 {
		return result, fmt.Errorf("empty")
	}
	return result, nil
}

// RPopList 右边移出 N 个
func (q *_queueStringList) RPopList(id any, count int) ([]string, error) {
	q.RWL.Lock()
	var result []string
	var j = 0
	for i := 0; i < count; i++ {
		j++
		temp, err := q.Redis.RPop(context.Background(), q.TopicKey(id)).Result()
		if err != nil || j > count*2 {
			if err == redis.Nil || j > count*2 {
				break
			}
			i--
			continue
		}
		result = append(result, temp)
	}
	q.RWL.Unlock()
	if len(result) <= 0 {
		return result, fmt.Errorf("empty")
	}
	return result, nil
}

// Count 获取队列当前长度
func (q *_queueStringList) Count(id any) (int64, error) {
	q.RWL.RLock()
	defer q.RWL.RUnlock()
	result, err := q.Redis.LLen(context.Background(), q.TopicKey(id)).Result()
	if err == nil || err == redis.Nil {
		return result, nil
	}
	return result, err
}

// GetAll 获取队列中所有的记录
func (q *_queueStringList) GetAll(id any) ([]string, error) {
	q.RWL.RLock()
	result, err := q.Redis.LRange(context.Background(), q.TopicKey(id), 0, -1).Result()
	q.RWL.RUnlock()
	return result, err
}

// ClearAll 清空所有的记录（直接删除key）
func (q *_queueStringList) ClearAll(id any) error {
	q.RWL.Lock()
	err := q.Redis.Del(context.Background(), q.TopicKey(id)).Err()
	q.RWL.Unlock()
	return err
}
